/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package MVC;

import common.Text;
import InterfaceUser.*;
import ChatNI.*;
import javax.swing.*;

import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;

import java.net.InetAddress;
import java.net.UnknownHostException;

import java.util.ArrayList;
import java.util.EventListener;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.Observable;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.event.*;
// horizontal scrolling
import static javax.swing.ScrollPaneConstants.*;

/**
 * ChatGUI
 * The view in the MVC pattern
 * Class in charge of organizing the interface with the LocalUser
 * This is the Observer of the ModelMessIn
 * @author Duy Duc, Metangmo
 */
public class ChatGUI extends JFrame implements ActionListener, WindowListener , MouseListener, FromUser , ToUser , java.util.Observer  {
    
    private String pseudo;
    private JPanel panel;
    // A chat tab for each user 
    private JTabbedPane panelOnglet;
    
    //The buttons
    private JButton join = new JButton("Attach a file");
    private JButton envoi = new JButton("Send");
    private JButton deconnect = new JButton("Disconnect");
    
    //The text areas
    private JTextArea aireEmission = new JTextArea (5,40);
    private JTextArea aireReception = new JTextArea (20,20);
    
    //The lists
    private DefaultListModel liste;
    private JList connectedListe ;
    //BiDi list
    private BidirectionalMap listeUser;
    //List of selected users who have already had a tab of conversation 
    private ArrayList<String> selectedUsers;
    //List of chosen users who will have a tab of conversation
    private ArrayList<String> chosenUsers;
    
    //MVC
    private ChatController controller;
    
    //Attributes complements
    private ColorOnglet color;
    private int compteur=0;
    private String nameGroup;
    private Boolean modeMulti=false;
    private String selectUserFile;
    
    /**
     * Constructor
     * @param pseudo
     *        Nickname of the LocalUser
     * @param controller
     *        Instance of the ChatController
     */

    public ChatGUI(String pseudo, ChatController controller) {
        this.pseudo = pseudo;
        this.controller = controller;
        this.initGUI();
        pack();
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(600,600);
        this.setVisible(true);
        // Title 
        this.setTitle("CHAT-SYSTEM :" +" ****"+ pseudo.toUpperCase()+ " *****");
        
    }


     private void initGUI() {
         
         // Title <<Online users>> appears at the top of the connected users' list
         JPanel labelList = new JPanel(new GridLayout(2, 1));
         JLabel lpers = new JLabel (" Online users" );
         
         //The panel is added in the Framce
         panel = new JPanel();
         add(panel);
         
          // Chat tab
          panelOnglet = new JTabbedPane();
          
          //The character's font in the emission and reception areas
          Font font = new Font("Verdana", Font.BOLD, 12);
         
         //Nickname of the LocalUser and GroupChatName
         this.pseudo=pseudo;
         this.nameGroup="";    
         
         //The lists
         liste = new DefaultListModel (); 
         listeUser = new BidirectionalMap();
         connectedListe  = new JList (liste);
         selectedUsers = new ArrayList<String>();
         chosenUsers = new ArrayList<String>();
         
          //Mode singleSelection by default
         connectedListe.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 
         
         //When a user is selected, his nickname turns blue
         connectedListe.setSelectionForeground(Color.BLUE);
         
	 //The labels
	 JPanel lesBoutons = new JPanel();
	 JPanel lesBoutons2 = new JPanel();
         
         //Color of the background of the emission and reception areas
	 aireEmission.setBackground(Color.white);
	 aireReception.setBackground(Color.white);
         
         //Font of the emission and reception areas
         aireEmission.setFont(font);
         aireEmission.setForeground(Color.BLUE);
         aireReception.setFont(font);
         aireReception.setForeground(Color.BLUE);
         
         
          //JTextArea is put in the JScrollPane in order to have a scroll bar
	  JScrollPane scroll = new JScrollPane(aireReception);
	  scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
	  aireReception.setEditable(false);
	  	      
	  // The lines will be wrapped if they are too long
          // to fit within the allocated width
          aireEmission.setLineWrap(true);
          aireReception.setLineWrap(true);

          
          // A chat tab is added in the panel of the conversations
          panelOnglet.addTab("About", null, scroll);
          panelOnglet.setBackgroundAt(0, Color.DARK_GRAY);
          
          //Color of a chat tab
          this.color = new ColorOnglet(panelOnglet);
          this.color.start();
		  
	  
	  //Parties of the label <<lesboutons2>>    
	  lesBoutons.add(join);
	  lesBoutons.add(deconnect);
          lesBoutons.add(envoi);
          
          ////Parties of the label <<lesboutons2>> 
          labelList.add(lpers);
          labelList.add(connectedListe);
          
	  add(lesBoutons2, BorderLayout.NORTH);
	  add(labelList, BorderLayout.EAST);
	  add(panelOnglet,BorderLayout.CENTER);
	  add(lesBoutons,BorderLayout.SOUTH);
          
          
	  //ActionListener is linked with the buttons
	  envoi.addActionListener(this);
          
	  deconnect.addActionListener(this); 
          
          join.addActionListener(this);
          
          addWindowListener(this);
          
          
          // MouseListener is linked with the liste of connected users
          this.connectedListe.addMouseListener(this);
           
     }
    
    /**
     * actionPerformed
     * function performs the different actions according to the pressed button
     * @param e
     *        ActionEvent
     */
     
    public void actionPerformed(ActionEvent e) {
                     //Button <<send>>
		      if (e.getSource() == envoi) {
                          try {
                              
                              // Collect the content of the JTextArea aireEmission and send it to the Controller
                              // Recipient is the nickname at the actuel chat tab 
                              // Recipient's IP Address is collected thanks to the information table in the ModelUser 
                              
                              Onglet ong = (Onglet)panelOnglet.getComponentAt(panelOnglet.getSelectedIndex());
                              
                              //If this is a GroupChatTab, the message is sent to all of the users in the group
                              if(this.nameGroup == panelOnglet.getTitleAt(panelOnglet.getSelectedIndex())){
                                  
                                    for (String s: chosenUsers){
                                        controller.performSendMessage(FabriqueMessage.TypeMessage.TEXT,listeUser.getKey(s), "Bill",Boolean.TRUE,ong.getAireEmission().getText());               
                                    }
                                    
                                    ong.getAireReception().append(" [MOI] : "+aireEmission.getText()+"\n");
                                    ong.getAireEmission().setText(null);
                              }
                              //If not, the message is sent normalement to the possessor of the actuel chat tab
                              else {
                                    controller.performSendMessage(FabriqueMessage.TypeMessage.TEXT,listeUser.getKey( panelOnglet.getTitleAt(panelOnglet.getSelectedIndex())), "Bill",Boolean.TRUE,ong.getAireEmission().getText());
                                    
                                    ong.getAireReception().append(" [MOI] : "+aireEmission.getText()+"\n");
                                    ong.getAireEmission().setText(null);
                              }
                              
                              
                          } catch (UnknownHostException ex) {
                              Logger.getLogger(ChatGUI.class.getName()).log(Level.SEVERE, null, ex);
                          }
		    	 
		    	 // Reset the JTextArea aireEmission
		    	 aireEmission.setText("");
		    	 
		      }
                      //Button <<Disconnect>>
                      else if (e.getSource() == deconnect){
                          //Phase disconnection
                          this.disconnect();
		      }
                      //Button <<Attach a file>>
                      else if (e.getSource() == join){
                          //File transfer
                          this.selectFile();
                      }
	   }   
  
    /**
     * deliverReceiveMessage:
     * function delivers the received message to the JTextArea aireReception
     * It needs the IP Address to create an associated chat tab
     * @param message
     *        Received message
     * @param address
     *        IP Address of the sender
     */
    public void deliverReceivedMessage(String message , InetAddress address)
    {
        Boolean trouve=false;
        int i=0;
        String user = listeUser.getValue(address);
        System.out.println( " Sender = " + user);
        
        // Verification the existance of this sender's chat tab
        for (i=0;i<panelOnglet.getTabCount();i++){
            //If yes, the message is sent to this tab
            if (panelOnglet.getTitleAt(i)== user){
                trouve=true;
                // The tab turns red to signify that a message arrived
                panelOnglet.setBackgroundAt(i, Color.RED);
              
                Onglet ong = (Onglet)panelOnglet.getComponentAt(i);
                ong.getAireReception().append("["+user+"] : "+ message +"\n");
                break;
            }
        }
        
        //If the tab doesn't exist, we create one for this user and the message is sent to this chat tab
        if (!trouve){
            selectedUsers.add(user);
            this.createOnglet (user);
            //The new tab turns yellow to signify the creation of a new chat tab including a message
            panelOnglet.setBackgroundAt(i, Color.YELLOW);
                
            Onglet ong = (Onglet)panelOnglet.getComponentAt(i);
            ong.getAireReception().append("["+user+"] : "+ message +"\n");
        }
    }
    
    /**
     * windowClosing:
     * function closes the ChatWindow and the system
     * @param arg0 
     *        WindowEvent
     */
    public void windowClosing(WindowEvent arg0) {
        this.disconnect();
    }
    
     
    @Override
    public void windowOpened(WindowEvent we) {
       // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void windowClosed(WindowEvent we) {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void windowIconified(WindowEvent we) {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void windowDeiconified(WindowEvent we) {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void windowActivated(WindowEvent we) {
       // throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void windowDeactivated(WindowEvent we) {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void connect(String username) {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
    
    /**
     * disconnect:
     * function performs the phase disconnection when the button <<Disconnect>> is pressed
     */
    @Override
    public void disconnect() {
        //Demand if the user really want to disconnect
        int reponse = JOptionPane.showConfirmDialog(this,
					       "Do you really want to disconnect from the ChatSystem ?",
					       "Avertissment", 
					        JOptionPane.YES_NO_OPTION);
                          //Yes-Option, the system stops
			  if (reponse == JOptionPane.YES_OPTION) {
                                try {
                                    //Window is closed
                                    controller.performSendMessage(FabriqueMessage.TypeMessage.BYE, InetAddress.getByName("255.255.255.255"),pseudo, null, null);
                                    controller.stop();
                                    System.exit(0);
                                } catch (UnknownHostException ex) {
                                    Logger.getLogger(ChatGUI.class.getName()).log(Level.SEVERE, null, ex);
                                }
				 
			 }
                         //No-Option, the system continues
			 else if (reponse == JOptionPane.NO_OPTION){
                              System.out.println("Just kidding! ^-^ ");
				  
			 }
        
    }

    @Override
    public void sendMessage() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
   
    /**
     * chooseMultiSelectionMode:
     * function give the LocalUser a chance to choose many users at the same time
     * to open a group chat or many chat tabs at once 
     * 
     */
    //Choisir le MultiSelection Mode
    public void chooseMultiSelectionMode(){
        
        int reponse = JOptionPane.showConfirmDialog(this,
					       pseudo + ", would you like to choose many users? ",
					       " **MULTI-SELECTION Demand** ", 
					        JOptionPane.YES_NO_OPTION);
        //Yes-Option, multi-selection mode is activated
	if (reponse == JOptionPane.YES_OPTION) {
                modeMulti= true;               
	
        //No-Option, nothing
	}
	else if (reponse == JOptionPane.NO_OPTION){
                modeMulti= false;
	}

        
    }
    
    /**
     * continueToChoose:
     * function permits the LocalUser to continue choosing the connected users or stop and choose to create a group chat or not
     */
    public void continueToChoose(){
        int reponse = JOptionPane.showConfirmDialog(this,
					       pseudo + ", would you like to choose another user? ",
					       " **CONTINUATION Demand** ", 
					        JOptionPane.YES_NO_OPTION);
        //Yes-Option, the LocalUser continue selecting the connected users
	if (reponse == JOptionPane.YES_OPTION) {
                compteur++;              
				 
	}
        //No-Option, a demand of creating group chat is given to the LocalUser
	else if (reponse == JOptionPane.NO_OPTION){
                int reponse2 = JOptionPane.showConfirmDialog(this,
					       pseudo + ", would you like to create a group chat? ",
					       " **GROUPCHAT Demand** ", 
					        JOptionPane.YES_NO_OPTION);
                
                //Yes-Option, all of the chosen users are put in a chat tab
                if (reponse2 == JOptionPane.YES_OPTION) {
                        this.nameGroup="";
                        for (String s: chosenUsers){
                            nameGroup = nameGroup + s;
                        }
                        createOnglet(nameGroup);

                }
                //No-Option, each chosen user is given a chat tab
                else if (reponse2 == JOptionPane.NO_OPTION){
                        for(String s: chosenUsers){               
                            createOnglet (s);
                        }
                        
                }
                //Reset the counter to zero
                compteur=0;
               
	} 
    
    }
    
    /**
     * selectUser:
     * function selects the chosen user and enregister him in a chosen list 
     */
    
    @Override
    public void selectUser() {
        
        //Chosen user
        String remoteUser;
        
        //Variable verifies the exisitance of this chosen user
        Boolean choisi=false;
                   
        remoteUser = (String) this.connectedListe.getSelectedValue();
        System.out.println("I choose " + remoteUser + " to open a conversation.");
                        
        //Verification the existance of thi chosen user in the chosen list
        for(String u2: chosenUsers){
            if(remoteUser == u2){
                 choisi = true;
                 break;
            }                   
        }

        //If this user isn't in the list, he is added in that list
        if (choisi == false){
             chosenUsers.add(remoteUser);
             selectedUsers.add(remoteUser);
        }
               
        System.out.println("*********choisenUsersList======" + chosenUsers);             
                   
        
    }
    
    /**
     * displayMessage:
     * function displays the received message in the About tab
     * @param message
     *        Received message
     */
    @Override
    public void displayMessage(String message) {
        panelOnglet.setBackgroundAt(0, Color.RED);
        aireReception.append(message+ "\n");
    }

    /**
     * mouseClicked:
     * function surveys the change to the online list and make an action whenever a change occurs
     * @param me
     *        MouseEvent
     */
    @Override
    public void mouseClicked(MouseEvent me) {
        // Variable verifies the existance of this sender's chat tab
        Boolean exist=false;
        //When there is a mouse click ( mouse press and mouse release )
        if (me.getButton() == MouseEvent.BUTTON1){
            
            //Verication the existance of this sender's chat tab
            for (String u1: selectedUsers){
                if ((String) this.connectedListe.getSelectedValue() == u1 ){
                    exist=true;
                    break;
                }
            }
            //If this user doesn't have a chat tab
            if (!exist){
                //For the premier choice, the LocalUser is demanded if he wants to use MultiSelection mode
                if ( compteur == 0){
                    this.chooseMultiSelectionMode();
                }

                
                //No-Option, a chat tab is created for this chosen user
                if(!modeMulti){
                    selectedUsers.add((String) this.connectedListe.getSelectedValue());
                    this.createOnglet((String) this.connectedListe.getSelectedValue());
                }
                //Yes-Option, the function selectUser is called
                //The LocalUser is asked to continue choosing the connected user whenever he clicks on a new one
                else{
                    this.selectUser();
                    this.continueToChoose();
                }
            
            }
            //If the chat tab exists, an annonce is given to the LocalUser
            else{
                System.out.println((String) this.connectedListe.getSelectedValue() + " have already had a tab of conversation");
            }
        }
        
    }

    @Override
    public void mousePressed(MouseEvent me) {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void mouseReleased(MouseEvent me) {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void mouseEntered(MouseEvent me) {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public void mouseExited(MouseEvent me) {
        //throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }
    
    /**
     * update:
     * functions receives the updated informations from the ModelMessIn and the ModelUser and treats these updates
     * @param o
     * @param obj 
     */
    
    @Override
    public void update(Observable o, final Object obj) {
        
        //The updated information is about the new users
        if (obj instanceof HashMap){
            
           SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                
            HashMap<InetAddress,String> obj2;
            obj2 = (HashMap)obj;
            
            liste.clear();
            //New user is added in a BiDi list
            for (InetAddress a: obj2.keySet()){
                listeUser.put(a,obj2.get(a));       
            }
            
            //New user is added in the online list
            //If it's the nickname of the LocalUser, it isn't added
            for (String s:obj2.values()){
                if ( s!= pseudo) {
                liste.addElement(s);
                }
            }
            System.out.println("OnlineList is updated!!!!");
            System.out.println("ListeUser = "+obj2);

         }

         });
            
        }
        //The updated information is a message
        else if (obj instanceof Text){
            Text obj2;
            obj2 = (Text)obj;
            //Fonction deliverReceivedMessage is called
            this.deliverReceivedMessage(obj2.getContent(),obj2.getAddr());
        }
        //The updated information is a notification
        else if (obj instanceof String){
            String mess = (String)obj;
            //Fonction displayMessage is called
            this.displayMessage(mess);
        }
    }
    
     /**
      * createOnglet:
      * function in charge of creating the chat tab
      * @param name
      *        Nickname of the user associated with the chat tab
      */
    
     public void createOnglet (String name) {
              
              System.out.println("New conversation is established with "+ name);

              Onglet onglet = new Onglet(this.controller,(InetAddress)listeUser.getKey(name));
              TitleOnglet title = new TitleOnglet(name,panelOnglet,this.selectedUsers);

              panelOnglet.addTab(name, null, onglet);
              panelOnglet.setTabComponentAt(panelOnglet.getTabCount()-1, title);
              panelOnglet.setBackgroundAt(panelOnglet.getTabCount()-1, Color.CYAN);
             
              System.out.println("Onglet creation finishes! selectedUsers======" + selectedUsers);
         
     }

    /**
     * selectFile:
     * function to give the LocalUser the chance to choose a file to send
     */
    @Override
    public void selectFile() {
        
        //Opening of the window for choosing a file
         JFileChooser dialogue = new JFileChooser();
         //Title
         dialogue.setDialogTitle(" File Choice ");
         //Display
         dialogue.showOpenDialog(null);
             
         System.out.println("Chosen file : " + dialogue.getSelectedFile());
         //Colect the chosen file
         controller.setFileToSend(dialogue.getSelectedFile());
         
         this.selectUserFile = panelOnglet.getTitleAt(panelOnglet.getSelectedIndex());
         
         //The name of the file's recipient is sent to the Controller too
         System.out.println("Name of the recipient :"+ this.selectUserFile);
        try {
            controller.performSendFile(FabriqueFile.TypeMessage.TRANSFERTFILEREQUEST,(InetAddress)listeUser.getKey(this.selectUserFile),0,null,null,null);
        } catch (UnknownHostException ex) {
            Logger.getLogger(ChatGUI.class.getName()).log(Level.SEVERE, null, ex);
        }
    
    }
    
    /**
     * fileReceiveDemand:
     * function opens a window to demand if the LocalUser want to receive a file from someone
     * @param username
     *        Nickname of the recipient
     */
    @Override
    public void fileReceiveDemand(String username) {
        
         int reponse = JOptionPane.showConfirmDialog(this,
					       username +" sent you a file. Would you like to receive this file ? ",
					       " **FILE RECEPTION Demand** ", 
					        JOptionPane.YES_NO_OPTION);
                          //Yes-Option, the LocalUser is demanded to choose the location of the file
			  if (reponse == JOptionPane.YES_OPTION) {
                                try {
                                    this.chooseFileLocation(username);

                                } catch (UnknownHostException ex) {
                                    Logger.getLogger(ChatGUI.class.getName()).log(Level.SEVERE, null, ex);
                                }
				 
			 }
                         //No-Option, a TREANSFERFILEACCEPT-FALSE is sent to the file's sender 
			 else if (reponse == JOptionPane.NO_OPTION){
                                try {
                                    controller.performSendFile(FabriqueFile.TypeMessage.TRANSFERTFILEACCEPT,(InetAddress)listeUser.getKey(username),0,null,Boolean.FALSE,null);
                                } catch (UnknownHostException ex) {
                                    Logger.getLogger(ChatGUI.class.getName()).log(Level.SEVERE, null, ex);
                                }
			 }
        
    }
    
    /**
     * chooseFileLocation:
     * function gives the LocalUser a chance to choose the location for the received file
     * @param username
     *        Nickname of the sender
     * @throws UnknownHostException 
     */
    public void chooseFileLocation(String username ) throws UnknownHostException {
        
        //Choice window is opened for choosing a place to store the file
         JFileChooser dialogue2 = new JFileChooser();
         
         dialogue2.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
            
         dialogue2.setDialogTitle("File Location Choice");
       
         //Display
         dialogue2.showOpenDialog(null);
         
         
         //Just the folder is chosen
         System.out.println(" Chosen folder: " + dialogue2.getSelectedFile());
          
         //File is added in the chosen folder
         controller.setDirectoryToStore(dialogue2.getSelectedFile());
         
         controller.performSendFile(FabriqueFile.TypeMessage.TRANSFERTFILEACCEPT,(InetAddress)listeUser.getKey(username),0,null,Boolean.TRUE,null);
    
     }  
}